package com.xxlie.spring.handler;

import com.xxlie.spring.annotation.Log;
import com.xxlie.spring.common.IpUtils;
import com.xxlie.spring.common.UserContext;
import com.xxlie.spring.entity.OperateLog;
import com.xxlie.spring.entity.User;
import com.xxlie.spring.exception.BusinessException;
import com.xxlie.spring.service.OperateLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author Cedric
 * 操作日志处理器
 */
@Slf4j
@Aspect
public class LogAopHandler {

    @Autowired
    private OperateLogService operateLogService;

    @Pointcut("execution(* com.xxlie.spring.controller..*.*(..))")
    private void controllerAspect(){}; // 定义切入点

    @Around("controllerAspect()")
    public Object around(ProceedingJoinPoint pjp) {

        // 拦截目标
        Object target = pjp.getTarget();
        // 拦截方法
        String methodName = pjp.getSignature().getName();

        // 拦截参数
        Object[] args = pjp.getArgs();

        String description = String.format("method=%s, args=%s", methodName, Arrays.toString(args));

        Signature signature = pjp.getSignature();

        MethodSignature methodSignature = null;

        if (signature instanceof MethodSignature) {
            methodSignature = (MethodSignature) signature;
        }

        Object object = null;

        OperateLog operateLog = buildOperateLog();

        try {

            if (methodSignature == null) {
                log.warn("获取方法签名失败");
                return null;
            }

            Class<?>[] parameterTypes = methodSignature.getMethod().getParameterTypes();

            Method method = target.getClass().getMethod(methodName, parameterTypes);

            if (Objects.isNull(method)) {
                return pjp.proceed();
            }

            if (Objects.isNull(method.getAnnotation(Log.class))) {
                return pjp.proceed();
            }

            Log log = method.getAnnotation(Log.class);
            operateLog.setModule(log.module());
            operateLog.setMethod(log.method());

            long start = System.currentTimeMillis();

            object = pjp.proceed();

            long end = System.currentTimeMillis();

            operateLog.setExecuteTime(end - start);

            operateLog.setResponseData("执行成功：" + description);

            operateLogService.addOperateLog(operateLog);

        } catch (BusinessException exception) {
            throw new BusinessException(exception.getMessage());
        } catch (Throwable throwable) {
            log.error("获取操作日志失败啦：Description = {}", description, throwable);
            operateLog.setResponseData("执行失败：" + description);
            operateLogService.addOperateLog(operateLog);
            throwable.printStackTrace();
            throw new BusinessException(throwable.getMessage());
        }

        return object;
    }

    private OperateLog buildOperateLog() {
        OperateLog operateLog = new OperateLog();
        User currentUser = UserContext.getCurrentUser();
        if (Objects.nonNull(currentUser)) {
            operateLog.setUserId(currentUser.getId());
            operateLog.setUserName(currentUser.getName());
        }

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        operateLog.setIpAddress(IpUtils.getIpAddr(request));
        return operateLog;
    }
}
